#include "MGUI_Helper.h"
#include "MGUI.h"

#pragma warning(push)
#pragma warning(disable : 4244)

namespace Myway {

	MGUI_Helper * MGUI_Helper::msInstance;

	MGUI_Helper::MGUI_Helper()
	{
		msInstance = this;

		mUnknownChar = L'*';

		mWhiteTexture = TextureManager::Instance()->CreateTexture("MGUI_White_Texture", 4, 4, 1);
		mBlackTexture = TextureManager::Instance()->CreateTexture("MGUI_Black_Texture", 4, 4, 1);

		LockedRect lr;
		mWhiteTexture->Lock(&lr, LOCK_WRITE);
		{
			unsigned int * pixel = (unsigned int *)lr.pData;

			for (int i = 0; i < 4 * 4; ++i)
			{
				pixel[i] = 0xFFFFFFFF;
			}
		}
		mWhiteTexture->Unlock();

		mBlackTexture->Lock(&lr, LOCK_WRITE);
		{
			unsigned int * pixel = (unsigned int *)lr.pData;

			for (int i = 0; i < 4 * 4; ++i)
			{
				pixel[i] = 0xFF000000;
			}
		}
		mBlackTexture->Unlock();
	}

	MGUI_Helper::~MGUI_Helper()
	{
		msInstance = NULL;

		for (int i = 0; i < mRenderItems.Size(); ++i)
			delete mRenderItems[i];

		mRenderItems.Clear();
	}

	MGUI_RectF MGUI_Helper::MapUVRect(const MGUI_Rect & _rect, Texture * _skin)
	{
		MGUI_RectF uv;

		if (_skin != NULL)
		{
			float invW = 1.0f / (_skin->GetWidth() - 1);
			float invH = 1.0f / (_skin->GetHeight() - 1);

			uv.x0 = (float)_rect.x0 * invW;
			uv.x1 = (float)_rect.x1 * invW;
			uv.y0 = (float)_rect.y0 * invH;
			uv.y1 = (float)_rect.y1 * invH;
		}

		return uv;
	}

	bool MGUI_Helper::Clip(MGUI_Rect & _orect, MGUI_RectF & _ouv, const MGUI_Rect & _rect, const MGUI_RectF & _uv, const MGUI_Rect & _clipRect)
	{
		if (_rect.DX() <= 0 || _rect.DY() <= 0)
			return false;

		if (_rect.x0 >= _clipRect.x1 || _rect.x1 <= _clipRect.x0 ||
			_rect.y0 >= _clipRect.y1 || _rect.y1 <= _clipRect.y0)
			return false;

		if (_rect.x0 >= _clipRect.x0 && _rect.x1 <= _clipRect.x1 &&
			_rect.y0 >= _clipRect.y0 && _rect.y1 <= _clipRect.y1)
		{
			_orect = _rect;
			_ouv = _uv;
		}
		else
		{
			float uStep = _uv.DX() / _rect.DX();
			float vStep = _uv.DY() / _rect.DY();
			float u = _uv.x0, v = _uv.y0;
			float x = _rect.x0, y = _rect.y0;

			_orect.x0 = Math::Maximum(_clipRect.x0, _rect.x0);
			_orect.y0 = Math::Maximum(_clipRect.y0, _rect.y0);
			_orect.x1 = Math::Minimum(_clipRect.x1, _rect.x1);
			_orect.y1 = Math::Minimum(_clipRect.y1, _rect.y1);

			_ouv.x0 = u + (_orect.x0 - x) * uStep;
			_ouv.y0 = v + (_orect.y0 - y) * vStep;
			_ouv.x1 = u + (_orect.x1 - x) * uStep;
			_ouv.y1 = v + (_orect.y1 - y) * vStep;
		}

		return true;
	}

	bool MGUI_Helper::Clip(MGUI_RectF & _orect, MGUI_RectF & _ouv, const MGUI_RectF & _rect, const MGUI_RectF & _uv, const MGUI_Rect & _clipRect)
	{
		if (_rect.DX() <= 0 || _rect.DY() <= 0)
			return false;

		if (_rect.x0 >= _clipRect.x1 || _rect.x1 <= _clipRect.x0 ||
			_rect.y0 >= _clipRect.y1 || _rect.y1 <= _clipRect.y0)
			return false;

		if (_rect.x0 >= _clipRect.x0 && _rect.x1 <= _clipRect.x1 &&
			_rect.y0 >= _clipRect.y0 && _rect.y1 <= _clipRect.y1)
		{
			_orect = _rect;
			_ouv = _uv;
		}
		else
		{
			MGUI_RectF clipRect;

			clipRect.x0 = (float)_clipRect.x0;
			clipRect.y0 = (float)_clipRect.y0;
			clipRect.x1 = (float)_clipRect.x1;
			clipRect.y1 = (float)_clipRect.y1;

			float uStep = _uv.DX() / _rect.DX();
			float vStep = _uv.DY() / _rect.DY();
			float u = _uv.x0, v = _uv.y0;
			float x = _rect.x0, y = _rect.y0;

			_orect.x0 = Math::Maximum(clipRect.x0, _rect.x0);
			_orect.y0 = Math::Maximum(clipRect.y0, _rect.y0);
			_orect.x1 = Math::Minimum(clipRect.x1, _rect.x1);
			_orect.y1 = Math::Minimum(clipRect.y1, _rect.y1);

			_ouv.x0 = u + (_orect.x0 - x) * uStep;
			_ouv.y0 = v + (_orect.y0 - y) * vStep;
			_ouv.x1 = u + (_orect.x1 - x) * uStep;
			_ouv.y1 = v + (_orect.y1 - y) * vStep;
		}

		return true;
	}

	const MGUI_Vertex * MGUI_Helper::GetQuad(const MGUI_Rect & _r, const MGUI_RectF & _uv, const MGUI_Color & _c)
	{
		static MGUI_Vertex q[4];

		q[0].x = _r.x0; q[0].y = _r.y0; q[0].u = _uv.x0; q[0].v = _uv.y0;
		q[0].r = _c.r; q[0].g = _c.g; q[0].b = _c.b; q[0].a = _c.a;
		
		q[1].x = _r.x1; q[1].y = _r.y0; q[1].u = _uv.x1; q[1].v = _uv.y0;
		q[1].r = _c.r; q[1].g = _c.g; q[1].b = _c.b; q[1].a = _c.a;

		q[2].x = _r.x0; q[2].y = _r.y1; q[2].u = _uv.x0; q[2].v = _uv.y1;
		q[2].r = _c.r; q[2].g = _c.g; q[2].b = _c.b; q[2].a = _c.a;

		q[3].x = _r.x1; q[3].y = _r.y1; q[3].u = _uv.x1; q[3].v = _uv.y1;
		q[3].r = _c.r; q[3].g = _c.g; q[3].b = _c.b; q[3].a = _c.a;

		const MGUI_Rect & rect = MGUI_Engine::Instance()->GetRect();

		float invWidth = 1.0f / (rect.DX());
		float invHeight = 1.0f / (rect.DY());

		for (int i = 0; i < 4; ++i)
		{
			q[i].x *= invWidth;
			q[i].y *= invHeight;

			q[i].x = q[i].x * 2 - 1;
			q[i].y = 1 - q[i].y * 2;
		}

		return q;
	}

	void MGUI_Helper::AddRenderItem(MGUI_RenderItem * ri, const MGUI_Rect & myRect, const MGUI_Rect & clRect,
		const MGUI_RectF & uvRect, const MGUI_RectF & uvClientRect, const MGUI_Color & color, const MGUI_Rect & clipRect)
	{
		MGUI_Rect rect, _rect;
		MGUI_RectF uv, _uv;

		// top
		if (clRect.y0 > 0)
		{
			// left
			if (clRect.x0 > 0)
			{
				rect.x0 = myRect.x0;
				rect.x1 = myRect.x0 + clRect.x0;

				rect.y0 = myRect.y0;
				rect.y1 = myRect.y0 + clRect.y0;

				uv.x0 = uvRect.x0;
				uv.x1 = uvClientRect.x0;

				uv.y0 = uvRect.y0;
				uv.y1 = uvClientRect.y0;

				if (Clip(_rect, _uv, rect, uv, clipRect))
					ri->AddQuad(MGUI_Helper::Instance()->GetQuad(_rect, _uv, color));
			}

			// center
			if (1)
			{
				rect.x0 = myRect.x0 + clRect.x0;
				rect.x1 = myRect.x0 + clRect.x1;

				rect.y0 = myRect.y0;
				rect.y1 = myRect.y0 + clRect.y0;

				uv.x0 = uvClientRect.x0;
				uv.x1 = uvClientRect.x1;

				uv.y0 = uvRect.y0;
				uv.y1 = uvClientRect.y0;

				if (Clip(_rect, _uv, rect, uv, clipRect))
					ri->AddQuad(MGUI_Helper::Instance()->GetQuad(_rect, _uv, color));
			}

			// right
			if (clRect.x1 < myRect.DX())
			{
				rect.x0 = myRect.x0 + clRect.x1;
				rect.x1 = myRect.x1;

				rect.y0 = myRect.y0;
				rect.y1 = myRect.y0 + clRect.y0;

				uv.x0 = uvClientRect.x1;
				uv.x1 = uvRect.x1;

				uv.y0 = uvRect.y0;
				uv.y1 = uvClientRect.y0;

				if (Clip(_rect, _uv, rect, uv, clipRect))
					ri->AddQuad(MGUI_Helper::Instance()->GetQuad(_rect, _uv, color));
			}
		}

		// center
		if (1)
		{
			// left
			if (clRect.x0 > 0)
			{
				rect.x0 = myRect.x0;
				rect.x1 = myRect.x0 + clRect.x0;

				rect.y0 = myRect.y0 + clRect.y0;
				rect.y1 = myRect.y0 + clRect.y1;

				uv.x0 = uvRect.x0;
				uv.x1 = uvClientRect.x0;

				uv.y0 = uvClientRect.y0;
				uv.y1 = uvClientRect.y1;

				if (Clip(_rect, _uv, rect, uv, clipRect))
					ri->AddQuad(MGUI_Helper::Instance()->GetQuad(_rect, _uv, color));
			}

			// center
			if (1)
			{
				rect.x0 = myRect.x0 + clRect.x0;
				rect.x1 = myRect.x0 + clRect.x1;

				rect.y0 = myRect.y0 + clRect.y0;
				rect.y1 = myRect.y0 + clRect.y1;

				uv.x0 = uvClientRect.x0;
				uv.x1 = uvClientRect.x1;

				uv.y0 = uvClientRect.y0;
				uv.y1 = uvClientRect.y1;

				if (Clip(_rect, _uv, rect, uv, clipRect))
					ri->AddQuad(MGUI_Helper::Instance()->GetQuad(_rect, _uv, color));
			}

			// right
			if (clRect.x1 < myRect.DX())
			{
				rect.x0 = myRect.x0 + clRect.x1;
				rect.x1 = myRect.x1;

				rect.y0 = myRect.y0 + clRect.y0;
				rect.y1 = myRect.y0 + clRect.y1;

				uv.x0 = uvClientRect.x1;
				uv.x1 = uvRect.x1;

				uv.y0 = uvClientRect.y0;
				uv.y1 = uvClientRect.y1;

				if (Clip(_rect, _uv, rect, uv, clipRect))
					ri->AddQuad(MGUI_Helper::Instance()->GetQuad(_rect, _uv, color));
			}
		}

		// right
		if (clRect.y1 < myRect.DY())
		{
			// left
			if (clRect.x0 > 0)
			{
				rect.x0 = myRect.x0;
				rect.x1 = myRect.x0 + clRect.x0;

				rect.y0 = myRect.y0 + clRect.y1;
				rect.y1 = myRect.y1;

				uv.x0 = uvRect.x0;
				uv.x1 = uvClientRect.x0;

				uv.y0 = uvClientRect.y1;
				uv.y1 = uvRect.y1;

				if (Clip(_rect, _uv, rect, uv, clipRect))
					ri->AddQuad(MGUI_Helper::Instance()->GetQuad(_rect, _uv, color));
			}

			// center
			if (1)
			{
				rect.x0 = myRect.x0 + clRect.x0;
				rect.x1 = myRect.x0 + clRect.x1;

				rect.y0 = myRect.y0 + clRect.y1;
				rect.y1 = myRect.y1;

				uv.x0 = uvClientRect.x0;
				uv.x1 = uvClientRect.x1;

				uv.y0 = uvClientRect.y1;
				uv.y1 = uvRect.y1;

				if (Clip(_rect, _uv, rect, uv, clipRect))
					ri->AddQuad(MGUI_Helper::Instance()->GetQuad(_rect, _uv, color));
			}

			// right
			if (clRect.x1 < myRect.DX())
			{
				rect.x0 = myRect.x0 + clRect.x1;
				rect.x1 = myRect.x1;

				rect.y0 = myRect.y0 + clRect.y1;
				rect.y1 = myRect.y1;

				uv.x0 = uvClientRect.x1;
				uv.x1 = uvRect.x1;

				uv.y0 = uvClientRect.y1;
				uv.y1 = uvRect.y1;

				if (Clip(_rect, _uv, rect, uv, clipRect))
					ri->AddQuad(MGUI_Helper::Instance()->GetQuad(_rect, _uv, color));
			}
		}
	}

	const MGUI_Vertex * MGUI_Helper::GetQuad(const MGUI_RectF & _r, const MGUI_RectF & _uv, const MGUI_Color & _c)
	{
		static MGUI_Vertex q[4];

		q[0].x = _r.x0; q[0].y = _r.y0; q[0].u = _uv.x0; q[0].v = _uv.y0;
		q[0].r = _c.r; q[0].g = _c.g; q[0].b = _c.b; q[0].a = _c.a;
		
		q[1].x = _r.x1; q[1].y = _r.y0; q[1].u = _uv.x1; q[1].v = _uv.y0;
		q[1].r = _c.r; q[1].g = _c.g; q[1].b = _c.b; q[1].a = _c.a;

		q[2].x = _r.x0; q[2].y = _r.y1; q[2].u = _uv.x0; q[2].v = _uv.y1;
		q[2].r = _c.r; q[2].g = _c.g; q[2].b = _c.b; q[2].a = _c.a;

		q[3].x = _r.x1; q[3].y = _r.y1; q[3].u = _uv.x1; q[3].v = _uv.y1;
		q[3].r = _c.r; q[3].g = _c.g; q[3].b = _c.b; q[3].a = _c.a;

		const MGUI_Rect & rect = MGUI_Engine::Instance()->GetRect();

		float invWidth = 1.0f / (rect.DX());
		float invHeight = 1.0f / (rect.DY());

		for (int i = 0; i < 4; ++i)
		{
			q[i].x *= invWidth;
			q[i].y *= invHeight;

			q[i].x = q[i].x * 2 - 1;
			q[i].y = 1 - q[i].y * 2;
		}

		return q;
	}

	MGUI_RenderItem * MGUI_Helper::GetEmptyRenderItem(int _order, Texture * _texture)
	{
		for (int i = 0; i < mRenderItems.Size(); ++i)
		{
			MGUI_RenderItem * item = mRenderItems[i];
			if (item->Empty())
			{
				item->Set(_order, _texture);
				return item;
			}
		}

		MGUI_RenderItem * item = new MGUI_RenderItem();

		item->Set(_order, _texture);

		mRenderItems.PushBack(item);

		return item;
	}

	MGUI_Widget * MGUI_Helper::CreateWidget(int type, const MGUI_LookFeel * lookfeel, MGUI_Widget * parent)
	{
		switch (type)
		{
		case MGUI_WidgetType::Widget:
			return new MGUI_Widget(lookfeel, parent);

		case MGUI_WidgetType::Button:
			return new MGUI_Button(lookfeel, parent);

		case MGUI_WidgetType::Label:
			return new MGUI_Label(lookfeel, parent);

		case MGUI_WidgetType::CheckBox:
			return new MGUI_CheckBox(lookfeel, parent);

		case MGUI_WidgetType::ComboBox:
			return new MGUI_ComboBox(lookfeel, parent);

		case MGUI_WidgetType::EditBox:
			return new MGUI_EditBox(lookfeel, parent);

		case MGUI_WidgetType::ListBox:
			return new MGUI_ListBox(lookfeel, parent);

		case MGUI_WidgetType::ImageBox:
			return new MGUI_ImageBox(parent);

		case MGUI_WidgetType::ProgressBar:
			return new MGUI_ProgressBar(lookfeel, parent);

		case MGUI_WidgetType::HScrollBar:
			return new MGUI_HScrollBar(lookfeel, parent);

		case MGUI_WidgetType::VScrollBar:
			return new MGUI_VScrollBar(lookfeel, parent);

		case MGUI_WidgetType::HSlider:
			return new MGUI_HSlider(lookfeel, parent);

		case MGUI_WidgetType::VSlider:
			return new MGUI_VSlider(lookfeel, parent);
		}

		return NULL;
	}

	int MGUI_Helper::GetWidgetType(MGUI_Widget * widget)
	{
		if (RTTI_TypeOf(MGUI_Widget, widget))
			return MGUI_WidgetType::Widget;
		else if (RTTI_TypeOf(MGUI_Button, widget))
			return MGUI_WidgetType::Button;
		else if (RTTI_TypeOf(MGUI_Label, widget))
			return MGUI_WidgetType::Label;
		else if (RTTI_TypeOf(MGUI_CheckBox, widget))
			return MGUI_WidgetType::CheckBox;
		else if (RTTI_TypeOf(MGUI_ComboBox, widget))
			return MGUI_WidgetType::ComboBox;
		else if (RTTI_TypeOf(MGUI_EditBox, widget))
			return MGUI_WidgetType::EditBox;
		else if (RTTI_TypeOf(MGUI_ListBox, widget))
			return MGUI_WidgetType::ListBox;
		else if (RTTI_TypeOf(MGUI_ImageBox, widget))
			return MGUI_WidgetType::ImageBox;
		else if (RTTI_TypeOf(MGUI_ProgressBar, widget))
			return MGUI_WidgetType::ProgressBar;
		else if (RTTI_TypeOf(MGUI_HScrollBar, widget))
			return MGUI_WidgetType::HScrollBar;
		else if (RTTI_TypeOf(MGUI_VScrollBar, widget))
			return MGUI_WidgetType::VScrollBar;
		else if (RTTI_TypeOf(MGUI_HSlider, widget))
			return MGUI_WidgetType::HSlider;
		else if (RTTI_TypeOf(MGUI_VSlider, widget))
			return MGUI_WidgetType::VSlider;
		else
			return MGUI_WidgetType::User;
	}

	MGUI_Rect MGUI_Helper::GetAlignRect(const MGUI_Rect & rect, const MGUI_Rect & parent, MGUI_Align align)
	{
		if (align._value == MGUI_Align::None)
			return rect;

		MGUI_Rect r = rect;

		int dx = rect.DX();
		int dy = rect.DY();
		int halfSizeX = rect.DX() / 2;
		int halfSizeY = rect.DY() / 2;
		int centerX = parent.CenterX();
		int centerY = parent.CenterY();

		if (align._value & MGUI_Align::Left)
		{
			r.x0 = parent.x0;
		}
		else if (align._value & MGUI_Align::Right)
		{
			r.x0 = parent.x1 - rect.DX();
		}

		if (align._value & MGUI_Align::Top)
		{
			r.y0 = parent.y0;
		}
		else if (align._value & MGUI_Align::Bottom)
		{
			r.y0 = parent.y1 - rect.DY();
		}

		if (align._value & MGUI_Align::HCenter)
		{
			r.x0 = centerX - halfSizeX;
		}

		if (align._value & MGUI_Align::VCenter)
		{
			r.y0 = centerY - halfSizeY;
		}

		r.x1 = r.x0 + dx;
		r.y1 = r.y0 + dy;

		if (align._value & MGUI_Align::HStretch)
		{
			r.x0 = parent.x0;
			r.x1 = parent.x1;
		}

		if (align._value & MGUI_Align::VStretch)
		{
			r.y0 = parent.y0;
			r.y1 = parent.y1;
		}

		return r;
	}

	MGUI_Rect MGUI_Helper::GetRelativeRect(MGUI_Widget * widget, const MGUI_Rect & absRect)
	{
		MGUI_Rect r;

		const MGUI_Rect & rect = widget->GetAbsRect();

		r.x0 = absRect.x0 - rect.x0;
		r.y0 = absRect.y0 - rect.y0;
		r.x1 = absRect.x1 - rect.x0;
		r.y1 = absRect.y1 - rect.y0;
		
		return r;
	}
}

#pragma warning(pop)
